package com.h2.ref.server.util;

import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.h2.util.image.RotatedImage;

public final class ImageUtils {
   
   private ImageUtils() {
      //utility constructor
   }
   
   private static final Logger LOG = LoggerFactory.getLogger(ImageUtils.class);

//   public static BufferedImage rotateGif(String uri, Double angle) throws IOException {
//      BufferedImage image = 
//         convertRGBAToIndexed(
//            ImageIO.read(new File(uri))
//            );
//      
//      AffineTransform at = new AffineTransform();
//      at.rotate(angle);
//      
//      BufferedImage result = new BufferedImage(
//            image.getWidth(), image.getHeight(),
//            BufferedImage.TYPE_BYTE_INDEXED);
//      Graphics2D  graphics = (Graphics2D) result.getGraphics();
//      graphics.drawImage(image, at, null);
//      
//      return result;
//   }
   
   public static BufferedImage rotate(String basePath, String uri,
         Double angle) throws IOException {
      
      if(uri == null || uri.length() == 0) {
         return null;
      }
      
      File imageFile = new File(basePath, uri);
      if(!imageFile.exists()) {
         LOG.error("File doesn't exist: " + imageFile);
         LOG.error("Absolute Path: " + imageFile.getAbsolutePath());
         LOG.error("Parent: " + new File(basePath).getAbsolutePath());
         return null;
      }
      
      BufferedImage image = ImageIO.read(new File(basePath + uri));
      
      AffineTransform at = new AffineTransform();
      at.rotate(angle, image.getWidth() / 2, image.getHeight() / 2);
      
      BufferedImage result = new BufferedImage(
            image.getWidth(), image.getHeight(),
            BufferedImage.TYPE_INT_ARGB);
      Graphics2D  graphics = (Graphics2D) result.getGraphics();
      graphics.drawImage(image, at, null);
      
      return result;
   }
   
   public static BufferedImage combineImages(String basePath,
         int minSize,
         RotatedImage... rImages) throws IOException {
      
      if(rImages == null) {
         return null;
      }
      else if(rImages.length == 1) {
         BufferedImage image = rotate(
               basePath, rImages[0].getUri(), rImages[0].getAngle());
         if(image == null) {
            image = newImage(minSize, minSize);
         }
         return image;
      }
      
      int maxWidth = minSize;
      int maxHeight = minSize;
      List<BufferedImage> images = new ArrayList<BufferedImage>(rImages.length);
      for (RotatedImage rImage : rImages) {
         BufferedImage image = rotate(
               basePath, rImage.getUri(), rImage.getAngle());
         if(image == null) {
            image = newImage(minSize, minSize);
         }
         images.add(image);
         if(image.getWidth() > maxWidth) {
            maxWidth = image.getWidth();
         }
         if(image.getHeight() > maxHeight) {
            maxHeight = image.getHeight();
         }
      }
      
      BufferedImage result = newImage(maxWidth, maxHeight);
      Graphics2D  graphics = (Graphics2D) result.getGraphics();
      for (BufferedImage image : images) {
         graphics.drawImage(image, 0, 0, null);
      }
      return result;
   }
   
   public static BufferedImage newImage(int maxWidth, int maxHeight) {
      return new BufferedImage(
            maxWidth, maxHeight,
            BufferedImage.TYPE_INT_ARGB);
   }
   
   public static BufferedImage buildImageArray(List<List<BufferedImage>> images,
         Integer imageSize, float baseScale) {
      
      float scale = imageSize * baseScale;
      int[] maxDimension = findImageDimension(images);
      
      int width = (int) Math.ceil(maxDimension[0] * scale);
      int height = (int) Math.ceil(maxDimension[1] * scale);
      
      if(width < 1 || height < 1) {
         LOG.error(String.format("Width (%d) and height (%d) cannot be <= 0",
               width, height));
         if(width < 1) {
            width = (int) Math.ceil(1 * scale);
         }
         else if(height < 1) {
            height = (int) Math.ceil(1 * scale);
         }
      }
      
      BufferedImage result = new BufferedImage(width, height,
            BufferedImage.TYPE_INT_RGB);
      Graphics2D  graphics = (Graphics2D) result.getGraphics();
      
      int x;
      int y;
      for (int i = 0; i < images.size(); i++) {
         List<BufferedImage> tileRow = images.get(i);
         for (int j = 0; j < tileRow.size(); j++) {
            BufferedImage image = tileRow.get(tileRow.size() - 1 - j);
            
            x = (int) Math.ceil(scale * i);
            y = (int) Math.ceil(scale * j);
            
            graphics.drawImage(image,
                  x, y, (int) Math.ceil(scale), (int) Math.ceil(scale), null);
         }
      }
      return result;
   }
   
   public static BufferedImage resize(BufferedImage image, Integer size) {
      
      
      BufferedImage result = new BufferedImage(size,
            size,
            BufferedImage.TYPE_INT_ARGB);
      Graphics2D  graphics = (Graphics2D) result.getGraphics();
      
      graphics.drawImage(image,
            0, 0, size, size, null);
      
      return result;
   }
   
   private static int[] findImageDimension(List<List<BufferedImage>> images) {
      
//      int[] maxDimension = new int[2];
//      for (int i = 0; i < images.size(); i++) {
//         List<BufferedImage> tileRow = images.get(i);
//         for (int j = 0; j < tileRow.size(); j++) {
//            BufferedImage image = tileRow.get(j);
//            if(image.getWidth() * j > maxDimension[0]) {
//               maxDimension[0] = image.getWidth() * j;
//            }
//            if(image.getHeight() * i > maxDimension[1]) {
//               maxDimension[1] = image.getHeight() * i;
//            }
//         }
//      }
      
      int[] maxDimension = new int[2];
      maxDimension[0] = images.size();
      maxDimension[1] = images.get(0).size();
      
      return maxDimension;
   }
   
   public static void writeImage(String fileName, String fileDir,
         RenderedImage rendImage) {
      File file = new File(fileDir, fileName + ".png");
      try {
         ImageIO.write(rendImage, "png", file);
      } catch (IOException exp) {
         // TODO Auto-generated catch block
         exp.printStackTrace();
      }
   }
   
   public static RenderedImage readImage(File file) {
      try {
         return ImageIO.read(file);
      } catch (IOException exp) {
         // TODO Auto-generated catch block
         exp.printStackTrace();
      }
      return null;
   }
   
//   public static BufferedImage convertRGBAToIndexed(BufferedImage src) {
//      BufferedImage dest = new BufferedImage(src.getWidth(), src.getHeight(),
//            BufferedImage.TYPE_BYTE_INDEXED);
//      Graphics g = dest.getGraphics();
//      g.setColor(new Color(231, 20, 189));
//      g.fillRect(0, 0, dest.getWidth(), dest.getHeight());
//      dest = makeTransparent(dest, 0, 0);
//      dest.createGraphics().drawImage(src, 0, 0, null);
//      return dest;
//   }
//
//   public static BufferedImage makeTransparent(BufferedImage image,
//         int x, int y) {
//      ColorModel cm = image.getColorModel();
//      if (!(cm instanceof IndexColorModel))
//         return image; // sorry...
//      IndexColorModel icm = (IndexColorModel) cm;
//      WritableRaster raster = image.getRaster();
//      // pixel is offset in ICM's palette
//      int pixel = raster.getSample(x, y, 0);
//      int size = icm.getMapSize();
//      byte[] reds = new byte[size];
//      byte[] greens = new byte[size];
//      byte[] blues = new byte[size];
//      icm.getReds(reds);
//      icm.getGreens(greens);
//      icm.getBlues(blues);
//      IndexColorModel icm2 = new IndexColorModel(8, size, reds, greens, blues,
//            pixel);
//      return new BufferedImage(icm2, raster, image.isAlphaPremultiplied(), null);
//   }
   
//   public static boolean writeCompressedPNG(BufferedImage image,
//         String formatName,
//         OutputStream output) {
//      
//      try {
//         PngEncoder encoder = new PngEncoder(image, true, 0, 0);
//         output.write(encoder.pngEncode());
//      } catch (IOException exp) {
//         LOG.error(exp.getMessage());
//         LOG.debug("Details: ", exp);
//         return false;
//      }
//      
//      return true;
//   }
   
}
